package com.changfeng.auth.filter;

import com.alibaba.fastjson.JSON;
import com.changfeng.auth.annotation.AuthPower;
import com.changfeng.auth.dto.UserAuth;
import com.changfeng.auth.util.JwtTokenUtil;
import com.changfeng.common.CurrentUser;
import com.changfeng.config.properties.JwtProperties;
import com.changfeng.exception.BizExceptionEnum;
import com.changfeng.exception.ChangFengException;
import com.changfeng.exception.ChangFengExceptionEnum;
import com.changfeng.handler.response.vo.ResponseVO;
import com.changfeng.tip.Tip;
import com.changfeng.util.RenderUtil;
import io.jsonwebtoken.JwtException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;

public class AuthFilter  extends OncePerRequestFilter implements ApplicationContextAware {

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    private ApplicationContext applicationContext;


    private String[] ignoreUrls = null;

    private Set<String> powerUrls = new HashSet<>();



    //初始化方法
    @PostConstruct
    private void init() {
        //初始化忽略列表
        String ignoreUrl = jwtProperties.getIgnoreUrl();
        if(ignoreUrl != null && !ignoreUrl.equals("") ){
            this.ignoreUrls = ignoreUrl.split(",");
        }

        Map<String, Object> beansWithAnnotationMap = this.applicationContext.getBeansWithAnnotation(RestController.class);
        Class<? extends Object> clazz = null;
        for(Map.Entry<String, Object> entry : beansWithAnnotationMap.entrySet()){
           clazz = entry.getValue().getClass();
            Method[] methods = clazz.getMethods();
            AuthPower authPower = null;
            for (Method method : methods) {
                authPower = method.getAnnotation(AuthPower.class);
                if (authPower != null) {
                    GetMapping getMapping = method.getAnnotation(GetMapping.class);
                    if(getMapping != null) {
                        for (String s : getMapping.value()) {
                            powerUrls.add(s);
                        }

                        continue;
                    }
                    PostMapping postMapping = method.getAnnotation(PostMapping.class);
                    if(postMapping != null) {
                        for (String s : postMapping.value()) {
                            powerUrls.add(s);
                        }

                        continue;
                    }
                }
             }
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (request.getServletPath().equals("/" + jwtProperties.getAuthPath())) {
            chain.doFilter(request, response);
            return;
        }

        //忽略部分url
        if(ignoreUrls != null && ignoreUrls.length >0) {
            for(String url : ignoreUrls) {
                if(request.getServletPath().equals(url)) {
                    chain.doFilter(request,response);
                    return;
                }
            }

        }

        final String requestHeader = request.getHeader(jwtProperties.getHeader());
        String authToken = null;
        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            authToken = requestHeader.substring(7);

            //通过token获取userId

            String userStr =jwtTokenUtil.getUsernameFromToken(authToken);
            UserAuth userAuth = JSON.parseObject(userStr,UserAuth.class);
            if(userStr == null) {
                return;
                // throw new NightmareException(BizExceptionEnum.TOKEN_ERROR);
            } else {
                //System.out.println(userStr);
                CurrentUser.saveUserId(userAuth);
            }

            //验证token是否过期,包含了验证jwt是否正确
            try {
                boolean flag = jwtTokenUtil.isTokenExpired(authToken);
                if (flag) {
                    RenderUtil.renderJson(response, ResponseVO.forEnum(BizExceptionEnum.TOKEN_EXPIRED));
                    return;
                }
            } catch (JwtException e) {
                //有异常就是token解析失败
                RenderUtil.renderJson(response, ResponseVO.forEnum(BizExceptionEnum.TOKEN_ERROR));
                return;
            }
            //对应权限验证
            if(powerUrls != null && powerUrls.size() >0) {
                for(String url : powerUrls) {
                    if(request.getServletPath().equals(url)) {
                        if (userAuth.getPowerId() != 1){
                            RenderUtil.renderJson(response, ResponseVO.forEnum(ChangFengExceptionEnum.NO_POWER));
                            //throw new ChangFengException(ChangFengExceptionEnum.NO_POWER);
                            return;
                        }
                    }
                }

            }

        } else {
            //header没有带Bearer字段
            RenderUtil.renderJson(response, ResponseVO.forEnum(BizExceptionEnum.TOKEN_ERROR));
            return;
        }
        chain.doFilter(request, response);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
